// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package textproto

import (
	"bufio"
	"fmt"
	"io"
)

// 编写器实现了方便的方法来编写对文本协议网络连接的请求或响应。
type Writer struct {
	W   *bufio.Writer
	dot *dotWriter
}

// NewWriter返回一个新的Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer Writer，该Writer。\r\n行
func NewWriter(w *bufio.Writer) *Writer {
	return &Writer{W: w}
}

var crnl = []byte{'\r', '\n'}
var dotcrnl = []byte{'.', '\r', '\n'}

func (w *Writer) PrintfLine(format string, args ...any) error {
	w.closeDot()
	fmt.Fprintf(w.W, format, args...)
	w.W.Write(crnl)
	return w.W.Flush()
}

// 当DotWriter关闭时。调用方应在下次调用w上的方法之前关闭
// DotWriter。
// 
// 有关点编码的详细信息，请参阅Reader的DotReader方法的文档。
func (w *Writer) DotWriter() io.WriteCloser {
	w.closeDot()
	w.dot = &dotWriter{w: w}
	return w.dot
}

func (w *Writer) closeDot() {
	if w.dot != nil {
		w.dot.Close() // 设置w.dot=nil 
	}
}

type dotWriter struct {
	w     *Writer
	state int
}

const (
	wstateBegin     = iota // 初始状态；必须为零
	wstateBeginLine        // 行首
	wstateCR               // 写入\r（可能在行尾）
	wstateData             // 在行中间写入数据
)

func (d *dotWriter) Write(b []byte) (n int, err error) {
	bw := d.w.W
	for n < len(b) {
		c := b[n]
		switch d.state {
		case wstateBegin, wstateBeginLine:
			d.state = wstateData
			if c == '.' {
				// 转义前导点
				bw.WriteByte('.')
			}
			fallthrough

		case wstateData:
			if c == '\r' {
				d.state = wstateCR
			}
			if c == '\n' {
				bw.WriteByte('\r')
				d.state = wstateBeginLine
			}

		case wstateCR:
			d.state = wstateData
			if c == '\n' {
				d.state = wstateBeginLine
			}
		}
		if err = bw.WriteByte(c); err != nil {
			break
		}
		n++
	}
	return
}

func (d *dotWriter) Close() error {
	if d.w.dot == d {
		d.w.dot = nil
	}
	bw := d.w.W
	switch d.state {
	default:
		bw.WriteByte('\r')
		fallthrough
	case wstateCR:
		bw.WriteByte('\n')
		fallthrough
	case wstateBeginLine:
		bw.Write(dotcrnl)
	}
	return bw.Flush()
}
